#ifndef __SOCKET_HPP_
#define __SOCKET_HPP_

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Logger.hpp"
#include "Addr.hpp"
#include <memory>
#include "NonBlock.hpp"

static const int _DefalutSockfd = -1;
static const int _backlog = 8;

// template method class
class Socket
{
public:
    virtual void CreateSocket() = 0;
    virtual void BindSocket(int port) = 0;
    virtual void Listen(int backlog) = 0;
    virtual void Close() = 0;
    virtual void Recv(std::string *buffer) = 0;
    virtual void Send(const std::string &buffer) = 0;
    virtual void Connect(Inet_Addr _server) = 0;
    virtual int SockFd() = 0;

    virtual int Accept(Inet_Addr *_ptr) = 0;
    virtual ~Socket()
    {
    }

public:
    virtual void BulidSocket(int _port)
    {
        CreateSocket();
        BindSocket(_port);
        Listen(_backlog);
    }
};

class TcpSocket : public Socket
{
public:
    TcpSocket()
        : _sockfd(_DefalutSockfd)
    {
    }

    TcpSocket(int sockfd)
        : _sockfd(sockfd)
    {
    }

    ~TcpSocket()
    {
    }

    void CreateSocket()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "create socket error" << "\r\n";
            return;
        }
        SetNonBlock(_sockfd);

        int opt = 1;

        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
    }

    void BindSocket(int _port)
    {
        Inet_Addr _addr(_port);
        if (bind(_sockfd, _addr.Addr(), _addr.Size()) != 0)
        {
            LOG(LogLevel::ERROR) << "bind socket error" << "\r\n";
            return;
        }
    }

    void Listen(int _backlog)
    {
        if (listen(_sockfd, _backlog) != 0)
        {
            LOG(LogLevel::ERROR) << "listen socket error" << "\r\n";
            return;
        }
    }

    int Accept(Inet_Addr *_Client)
    {
        struct sockaddr_in _peer;
        socklen_t _len = sizeof(_peer);

        int _fd = accept(_sockfd, (struct sockaddr *)&_peer, &_len);
        if (_fd < 0)
        {
            LOG(LogLevel::ERROR) << "sockfd accept error" << "\r\n";
            return -1;
        }

        _Client->ReInit(_peer);
        return _fd;
    }

    void Close()
    {
        close(_sockfd);
    }

    void Recv(std::string *buffer)
    {
        while (1)
        {
            char out[1024] = {0};
            ssize_t n = recv(_sockfd, out, sizeof(out) - 1, 0);
            if (n > 0)
            {
                out[n] = 0;
                *buffer += out;
                std::cout << buffer;
                break;
            }
            else if (n == 0)
            {
                sleep(1);
                LOG(LogLevel::INFO) << "对端关闭" << "\r\n";
                return;
            }
            else
            {
                if (errno == EAGAIN)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                LOG(LogLevel::ERROR) << "读取错误" << "\r\n";
                break;
            }
        }
    }

    void Send(const std::string &buffer)
    {
        int n = send(_sockfd, buffer.c_str(), buffer.size(), 0);
        if (n < 0)
        {
            LOG(LogLevel::WARNING) << "发送失败" << "\r\n";
            return;
        }
    }

    void Connect(Inet_Addr _server)
    {
        if (connect(_sockfd, _server.Addr(), _server.Size()) != 0)
        {
            LOG(LogLevel::ERROR) << "connect server error" << "\r\n";
            return;
        }
    }

    int SockFd()
    {
        return _sockfd;
    }

private:
    int _sockfd;
};

#endif